﻿using System;
using System.Collections.Generic;
using System.Linq;
using BBCode.Nodes;
using BBCode.Template;

namespace BBCode.Tags
{
    //Default type
    /// <summary>
    /// The default BBCode Type
    /// </summary>
    public class BBCode : BBCode<Nodes.BBCode>
    {
        public BBCode(string name, string templateName, bool canHaveChildren = true, int priority = 0)
            : base(name, templateName, canHaveChildren, priority)
        {
        }

        public new IEnumerable<INode> GetNodes(string text)
        {
            return base.GetNodes(text);
        }
    }


    //Generic type
    /// <summary>
    /// Generic BBCode Tag.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class BBCode<T> : ITag where T : INode
    {
        private readonly bool _canHaveChildren;
        protected readonly string _closingTag;
        private readonly string _name;
        private readonly string _openingTag;
        private readonly int _priority;
        private readonly string _templateName;

        /// <summary>
        /// Initializes a new instance of the <see cref="BBCode&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="templateName">Name of the template.</param>
        /// <param name="canHaveChildren">if set to <c>true</c> [can have children].</param>
        /// <param name="priority">The priority.</param>
        public BBCode(string name, string templateName, bool canHaveChildren = true, int priority = 0)
        {
            _name = name;
            _openingTag = "[" + name + "]";
            _closingTag = "[/" + name + "]";
            _canHaveChildren = canHaveChildren;
            _templateName = templateName;
            _priority = priority;
        }

        #region ITag Members

        /// <summary>
        /// Gets the name of the template.
        /// </summary>
        /// <value>
        /// The name of the template.
        /// </value>
        public string TemplateName
        {
            get { return _templateName; }
        }

        /// <summary>
        /// Gets the template.
        /// </summary>
        /// <returns>THe NVTemplate</returns>
        public NVTemplate GetTemplate()
        {
            return new NVTemplate(TemplateName);
        }

        /// <summary>
        /// Gets a value indicating whether this instance can have children.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance can have children; otherwise, <c>false</c>.
        /// </value>
        public bool CanHaveChildren
        {
            get { return _canHaveChildren; }
        }

        /// <summary>
        /// Gets the name.
        /// </summary>
        public string Name
        {
            get { return _name; }
        }

        /// <summary>
        /// Gets or sets the type of the parse.
        /// </summary>
        /// <value>
        /// The type of the parse.
        /// </value>
        public ParseType ParseType { get; set; }

        /// <summary>
        /// Gets the priority.
        /// </summary>
        public int Priority
        {
            get { return _priority; }
        }

        /// <summary>
        /// Gets the nodes.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns>Returns the found nodes.</returns>
        public IEnumerable<INode> GetNodes(string text)
        {
            IList<INode> results = new List<INode>();

            var index = text.IndexOf(_openingTag, StringComparison.OrdinalIgnoreCase);
            var openings = new List<int>();
            while (index != -1)
            {
                openings.Add(index);
                index = text.IndexOf(_openingTag, index + 1, StringComparison.OrdinalIgnoreCase);
            }

            index = text.IndexOf(_closingTag, StringComparison.OrdinalIgnoreCase);
            var closings = new List<int>();
            while (index != -1)
            {
                closings.Add(index);
                index = text.IndexOf(_closingTag, index + 1, StringComparison.OrdinalIgnoreCase);
            }

            //Clean the closing tags
            for (var i = 0; i < closings.Count; i++)
            {
                if (closings[i] >= openings[0])
                {
                    if (i != 0)
                    {
                        closings.RemoveRange(0, closings.IndexOf(closings[i]) - 1);
                    }
                    break;
                }
            }

            //Clean the opening tags
            for (var i = openings.Count - 1; i >= 0; i--)
            {
                if (openings[i] <= closings[closings.Count - 1])
                {
                    if (i != openings.Count - 1)
                    {
                        closings.RemoveRange(openings.IndexOf(openings[i]) + 1, openings.Count - 1);
                    }
                    break;
                }
            }

            while (closings.Count > 0)
            {
                var openingIndex = (from o in openings
                                    where o < closings[0]
                                    select o).Max();

                //Find original indexes
                var nodeText = text.Substring(openingIndex, closings[0] - openingIndex + _closingTag.Length);
                var nodeIndex = new NodeIndex {Beginning = openingIndex, End = closings[0] + _closingTag.Length};

                //We have ourself a match!
                var bbCodeNode =
                    (T)
                    Activator.CreateInstance(typeof (T),
                                             new object[] {this, nodeText, nodeIndex, _openingTag, _closingTag});
                results.Add(bbCodeNode);

                closings.RemoveAt(0);
                openings.Remove(openingIndex);
            }

            return results;
        }

        #endregion
    }
}